use crate::modbus::mcrc;
use crate::util;
use crate::common::cs;

/// Modbus Rtu规范实体\
/// 构造支持from[u8;6]以及from[u8;8]
#[derive(Debug, Clone)]
pub struct ModbusRtu {
    pub address: u8,
    pub function: u8,
    pub data_address: [u8; 2],
    pub data: [u8; 2],
    pub crc: [u8; 2],
}

impl From<&[u8; 6]> for ModbusRtu {
    fn from(u: &[u8; 6]) -> Self {
        let mut r = [0u8; 8];
        let checked = mcrc::modbus_check(u);
        r[..6].clone_from_slice(&u[..6]);
        r[6..8].clone_from_slice(&checked[..]);
        Self::from(&r)
    }
}

impl From<&[u8; 8]> for ModbusRtu {
    fn from(u: &[u8; 8]) -> Self {
        Self {
            address: u[0],
            function: u[1],
            data_address: [u[2], u[3]],
            data: [u[4], u[5]],
            crc: [u[6], u[7]],
        }
    }
}

impl ModbusRtu {
    pub fn new(address: u8, function: u8, data_address: u16, data: u16) -> ModbusRtu {
        let data_address = util::u16_to_u8_slice(data_address);
        let data = util::u16_to_u8_slice(data);
        let u = [
            address,
            function,
            data_address[0],
            data_address[1],
            data[0],
            data[1]
        ];
        Self::from(&u)
    }

    pub fn to_slice(&self) -> [u8; 8] {
        [
            self.address,
            self.function,
            self.data_address[0],
            self.data_address[1],
            self.data[0],
            self.data[1],
            self.crc[0],
            self.crc[1]
        ]
    }

    pub fn to_box(&self) -> Box<[u8]> {
        self.to_slice().to_vec().into_boxed_slice()
    }
}

/// Modbus Rtu协议的返回
#[derive(Debug, Clone)]
pub struct ModbusRtuResult {
    pub address: u8,
    pub function: u8,
    pub effective: u8,
    pub data: Vec<u8>,
    pub crc: [u8; 2],
}

impl ModbusRtuResult {
    pub fn from(u: &[u8]) -> cs::Result<Self> {
        if u.len() < 5 {
            return Err(cs::Error::new(
                format!("modbus rtu result length is insufficient :\
                need 5 left , but length = `{}`", u.len()))
            );
        };
        if !mcrc::modbus_check_result(u) {
            return Err(cs::Error::new("modbus rtu result check crc failed"));
        }
        let mut data = vec![];
        for x in u.iter().take(3 + u[2] as usize).skip(3) {
            data.push(*x);
        }
        let result = Self {
            address: u[0],
            function: u[1],
            effective: u[2],
            data,
            crc: [u[u.len() - 2], u[u.len() - 1]],
        };
        Ok(result)
    }

    pub fn to_slice(&self) -> Box<[u8]> {
        let mut result = vec![
            self.address,
            self.function,
            self.effective,
        ];
        result.append(&mut self.data.clone());
        result.push(self.crc[0]);
        result.push(self.crc[1]);
        result.into_boxed_slice()
    }
}

#[cfg(test)]
mod tests {
    use crate::modbus::basic::{ModbusRtu, ModbusRtuResult};
    use crate::modbus::mcrc;

    #[test]
    fn test_modbus_rtu() {
        let mr = ModbusRtu::new(0x01, 0x03, 0x0003, 0x0001);
        println!("{:?}", mr.to_slice());
        let mr = ModbusRtu::from(&[0x01, 0x03, 0x00, 0x03, 0x00, 0x01]);
        println!("{:?}", mr.to_slice());
    }

    #[test]
    fn test1() {
        let mut origin = vec![0x01, 0x03, 0x04, 0x00, 0x01, 0xaf, 0xaa];
        let result = mcrc::modbus_check_slice(&origin);
        for x in result {
            origin.push(x);
        }

        let mrr = ModbusRtuResult::from(origin.as_slice()).unwrap();
        println!("{:?}", mrr);
        println!("{:?}", mrr.to_slice());
    }

    #[test]
    fn test2() {
        let result = [0x01, 0x03, 0x06, 0x02, 0x92, 0xff, 0x9b, 0x03, 0xe8, 0xd8, 0x0f];
        let rtu_result = ModbusRtuResult::from(&result);
        println!("{:?}", rtu_result);
    }
}